# !/usr/bin/python
# coding: utf8

import json
import datetime
import re
from json import JSONDecoder, JSONEncoder

FLAGS = re.VERBOSE | re.MULTILINE | re.DOTALL
WHITESPACE = re.compile(r'[ \t\n\r]*', FLAGS)


class ComplexJsonEncoder(JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S.%f')
        elif isinstance(obj, bytes):
            return obj.decode('utf-8')

        return JSONEncoder.default(self, obj)

    def obj_deal(self, obj):
        if isinstance(obj, list):
            tmp_list = []
            for o in obj:
                tmp_list.append(self.obj_deal(o))
            obj = tmp_list
        elif isinstance(obj, dict):
            obj = {
                k.decode('utf-8')
                if isinstance(k, (bytes, bytearray))
                else k: json.loads(v, cls=ComplexJsonDecoder)
                if isinstance(v, (bytes, bytearray))
                else v
                for k, v in obj.items()
            }

        return obj

    def encode(self, obj):
        try:
            obj = {
                k.decode('utf-8')
                if isinstance(k, (bytes, bytearray))
                else k: json.loads(v, cls=ComplexJsonDecoder)
                if isinstance(v, (bytes, bytearray))
                else v
                for k, v in obj.items()
            }
        except AttributeError:
            obj = self.obj_deal(obj)

        return super(ComplexJsonEncoder, self).encode(obj)


class ComplexJsonDecoder(JSONDecoder):
    def decode(self, s, _w=WHITESPACE.match):
        try:
            if isinstance(s, (str, bytes, bytearray)):
                s = s.replace('None', 'null')
            return super(ComplexJsonDecoder, self).decode(s)
        except ValueError:
            pass
        return s
